﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dijkstra.NET.Graph;
using Dijkstra.NET.Graph.Simple;
using Dijkstra.NET.ShortestPath;
using DijkstraLibrary.Models;
using FileLibrary;
using FileLibrary.Enums;
using FileLibrary.Models;
using Path = FileLibrary.Models.Path;

namespace DijkstraLibrary
{
    public class DijkstraManage
    {
        private readonly List<Track> _tracks;
        private readonly List<Path> _paths;
        private Dictionary<string, List<Route>> routeGroupDictionary = new Dictionary<string, List<Route>>();
        private Dictionary<Track, bool> visited = new Dictionary<Track, bool>();
        private Stack<Track> stack = new Stack<Track>();
        private Track dest;
        private Track source;

        public DijkstraManage(List<Track> tracks, List<Path> paths)
        {
            _tracks = tracks;
            _paths = paths;
            Load();
        }

        public bool Load()
        {
            if (_tracks == null || _paths == null) return false;
            Graph = new Graph<int, string>();
            foreach (var track in _tracks)
            {
                Graph.AddNode(track.Index);
            }

            var addPathFlag = true;
            foreach (var path in _paths)
            {
                var from = _tracks.FirstOrDefault(x => x.Name == path.FromTrackName);
                var to = _tracks.FirstOrDefault(x => x.Name == path.ToTrackName);
                if (from != null && to != null)
                {
                    Graph.Connect((uint)from.Index, (uint)to.Index, path.Cost, null);
                }
                else
                {
                    addPathFlag = false;
                    break;
                }
            }

            //计算检修径路
            var inAndOutTrack = _tracks.Where(x => x.TrackTypeEnum == TrackTypeEnum.InAndOutTrack).ToList();
            var repairTrack = _tracks.Where(x => x.TrackTypeEnum == TrackTypeEnum.Repair).ToList();
            var index = 1;
            foreach (var inAndOut in inAndOutTrack)
            {
                foreach (var repair in repairTrack)
                {
                    //var result = Graph.Dijkstra((uint)inAndOut.Index, (uint)repair.Index);
                    //var path = result.GetPath();
                    //routes.Add(new Route()
                    //{
                    //    Index = index++,
                    //    From = inAndOut,
                    //    To = repair,
                    //    Paths = path.Select(x => _tracks.First(y => y.Index == x)).ToList()
                    //});

                    Search(inAndOut, repair);
                }
            }

            return addPathFlag;
        }

        private void Search(Track startingVertex, Track repair)
        {
            source = startingVertex;
            dest = repair;
            var vertices = _tracks;
            visited = new Dictionary<Track, bool>();
            vertices.ForEach(v => visited.Add(v, false));
            // Create a stack for DFS 
            stack = new Stack<Track>();
            // Mark the current vertex as visited and push it on top of stack 
            DFS(startingVertex);
        }

        private void DFS(Track startingVertex)
        {
            //Check if currentVertex meets search criteria
            if (startingVertex.Name == dest.Name)
            {
                var tracks = stack.ToList();
                tracks.Reverse();
                tracks.Add(startingVertex);
                var key = $"{source.Name}-{dest.Name}";
                if (!routeGroupDictionary.ContainsKey(key))
                {
                    routeGroupDictionary.Add(key, new List<Route>());
                }

                var route = routeGroupDictionary[key];
                route.Add(new Route()
                {
                    Paths = tracks
                });

                return;
            }

            visited[startingVertex] = true;
            stack.Push(startingVertex);

            //push all non visited vertices on the stack
            foreach (var path in _paths)
            {
                if (path.FromTrackName == startingVertex.Name)
                {
                    var vertexToQueue = _tracks.First(x => x.Name == path.ToTrackName);
                    if (visited.ContainsKey(vertexToQueue) && !visited[vertexToQueue])
                    {
                        DFS(vertexToQueue);
                    }
                }
            }

            visited[startingVertex] = false;
            stack.Pop();
        }

        public Graph<int, string> Graph { get; set; }
    }
}
